package com.googlecode.kohmori.reportgen;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.sql.Connection;

import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;

import com.googlecode.kohmori.springmvc.LoginReportParams;
import com.googlecode.kohmori.springmvc.DefaultReportParams;

import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.Fop;
import org.xml.sax.InputSource;

public abstract class AbstractReportGenerator {
   protected AbstractXMLBuilder xmlBuilder;
   protected AbstractSQLSource sqlSource = null;
   protected String xslFilePath;
   protected String testXMLFilePath;
   private static URL dbFileURL = null;
   protected String reportType = null; // set to PDF, RTF, etc. in subclasses

   protected void addTransformerParameters(Transformer transformer,
         DefaultReportParams rP) {
   }

   public AbstractXMLBuilder getXMLBuilder() {
      return xmlBuilder;
   }

   public void setXMLBuilder(AbstractXMLBuilder xmlBuilder) {
      this.xmlBuilder = xmlBuilder;
   }

   public String getTestXMLFilePath() {
      return testXMLFilePath;
   }

   public void setTestXMLFilePath(String testXMLFilePath) {
      this.testXMLFilePath = testXMLFilePath;
   }

   public String getXslFilePath() {
      return xslFilePath;
   }

   public void setXslFilePath(String xslFilePath) {
      this.xslFilePath = xslFilePath;
   }

   public AbstractSQLSource getSqlSource() {
      return sqlSource;
   }

   public void setSqlSource(AbstractSQLSource sqlSource) {
      this.sqlSource = sqlSource;
   }

   protected JDBCConnObj getJDBCConnObj() throws Exception {
      if (dbFileURL == null) {
         dbFileURL = getClass().getClassLoader().getResource("databases.xml");

         if (dbFileURL == null) {
            throw new IllegalStateException(
                  "Cannot find database listing file.");
         }
      }
      return new JDBCConnObj(dbFileURL);
   }

   public void createSourceXMLFile(String fileName, LoginReportParams lRP)
         throws Exception {
      JDBCConnObj jco = getJDBCConnObj();

      if (jco.connectToDatabase(lRP.getUsername(), lRP.getPassword(), lRP
            .getDatabaseName()) == true) {
         Connection conn = jco.getDBConn();
         createSourceXMLFile(fileName, conn, lRP);
      }
   }

   public void createSourceXMLFile(String fileName, Connection conn,
         DefaultReportParams dRP) throws Exception {
      InputSource rpt = sqlSource.createReportObject(conn, dRP);
      Source rptSource = new SAXSource(xmlBuilder, rpt);
      createLocalXMLFile(rptSource, fileName);
   }

   protected static void createLocalXMLFile(Source src, String fileName) {
      TransformerFactory factory = TransformerFactory.newInstance();
      try {
         Transformer transformer = factory.newTransformer();
         Result res = new StreamResult(fileName);
         transformer.transform(src, res);
         System.out.println("File " + fileName + " has been created.");
      } catch (Exception e) {
         System.out.println("Transformer exception: " + e.getMessage());
      }
   }

   public ByteArrayOutputStream createReportBAOS(Connection conn,
         DefaultReportParams rP) throws Exception {
      ByteArrayOutputStream reportBaos = new ByteArrayOutputStream();
      OutputStream out = new java.io.BufferedOutputStream(reportBaos);
      createReport(conn, rP, out, null);
      return reportBaos;
   }

   public void createReportFile(Connection conn, DefaultReportParams rP,
         String reportFileName) throws Exception {
      createReport(conn, rP, null, reportFileName);
   }

   private void createReport(Connection inConn, DefaultReportParams rP,
         OutputStream outStream, String reportFileName) throws Exception {
      Connection conn;
      TransformerFactory factory = TransformerFactory.newInstance();
      boolean providedConn = false;

      if (inConn == null) { // using login parameters
         JDBCConnObj jco = getJDBCConnObj();
         if (rP instanceof LoginReportParams) {
            LoginReportParams lRP = (LoginReportParams) rP;
            jco.connectToDatabase(lRP.getUsername(), lRP.getPassword(), lRP
                  .getDatabaseName());
         } else {
            throw new IllegalArgumentException(
                  "(internal error) No database "
                        + "connection provided; report params must extend LoginReportParams");
         }
         conn = jco.getDBConn();
      } else {
         providedConn = true;
         conn = inConn;
      }

      InputSource rpt = sqlSource.createReportObject(conn, rP);
      Source rptSource = new SAXSource(xmlBuilder, rpt);
      URL stylesheetURL;
      if (xslFilePath != null) {
         stylesheetURL = getClass().getClassLoader().getResource(xslFilePath);
         if (stylesheetURL == null) {
            throw new IllegalArgumentException(
                  "Error:  Cannot find resource for xslFilePath = "
                        + xslFilePath);
         }
      } else {
         throw new IllegalArgumentException(
               "Error:  xslFilePath not configured.");
      }
      Transformer transformer = factory.newTransformer(new StreamSource(
            stylesheetURL.toString()));
      addTransformerParameters(transformer, rP);

      if (rP.getCreateSourceXMLFile()) {
         System.out.println("Spooling output (kohmoritest.xml)");
         // can add directory location also (i.e.,
         // '"/home/abc/kohmoritest.xml'");
         createLocalXMLFile(rptSource, "kohmoritest.xml");
      }
      // for debugging: Use the test hardcoded XML file.
      if (rP.getUseTestFile()) {
         if (testXMLFilePath == null || testXMLFilePath.length() == 0) {
            throw new IllegalStateException("No test XML data provided.");
         }
         rptSource = new StreamSource(testXMLFilePath);
         System.out.println("Using test file " + testXMLFilePath
               + " to generate output report.");
      }
      if (reportFileName != null) {
         outStream = new java.io.FileOutputStream(reportFileName);
      }

      FopFactory fopFactory = FopFactory.newInstance();
      Fop fop = fopFactory.newFop(reportType, outStream);
      Result res = new SAXResult(fop.getDefaultHandler());

      transformer.transform(rptSource, res);
      outStream.close();
      if (!providedConn) {
         conn.close();
      }
   }

}
